﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Reflection;

using Newtonsoft.Json;

namespace Webprox
{
    public abstract class ProxyInvoker : IHttpHandler
    {
        #region 调用器的私有区域

        /***********************************************************
         *                    枚举或数据结构定义
         ***********************************************************/

        /// <summary>
        /// 接口的调用者类型
        /// </summary>
        public enum ERequestPlatform
        {
            Desktop,
            Mobile,
            Web
        }

        /***********************************************************
         *                      属性和方法定义
         ***********************************************************/

        /// <summary>
        /// 本次调用的返回类型
        /// </summary>
        private ERequestPlatform _reqtype;
        /// <summary>
        /// 是否需要验证
        /// </summary>
        private bool _isValidate;
        /// <summary>
        /// 模块所在的命名空间
        /// </summary>
        private string _moduleNamespace;
        /// <summary>
        /// response
        /// </summary>
        private HttpResponse _response = null;

        #endregion

        #region 本次调用器的属性

        /// <summary>
        /// 调用接口的平台
        /// </summary>
        public ERequestPlatform ReqType { get { return _reqtype; } }
        /// <summary>
        /// 是否需要验证
        /// </summary>
        public bool IsValidate { get { return _isValidate; } }


        #endregion

        #region 辅助方法

        /// <summary>
        /// 获取方法里面的attribute信息
        /// </summary>
        /// <param name="m">方法</param>
        /// <returns></returns>
        private WebproxyAttribute _getAttribute(MethodInfo m)
        {
            if (m == null)
                return null;

            WebproxyAttribute attr = null;
            var attrs_ = m.GetCustomAttributes(typeof(WebproxyAttribute), false);
            if (attrs_ != null)
            {
                attr = (WebproxyAttribute)attrs_[0];
            }
            return attr;

        }
        /// <summary>
        /// 根据忽略字段调整输出的返回数据
        /// </summary>
        /// <param name="mr"></param>
        /// <returns></returns>
        private MetaResult _trimResultData(MetaResult mr, string[] trimfield)
        {
            // 如果没有data则直接返回
            if (mr == null || mr.Data == null || trimfield == null || trimfield.Length == 0)
                return mr;

            // 如果有的则转换为jobject，如果不能转换就别管了，能转换则再trim
            if (mr.Data is Newtonsoft.Json.Linq.JObject)
            {
                var table = (Newtonsoft.Json.Linq.JObject)mr.Data;
                // 不能用Contais比较，因为没有containskey,只能够一个一个确定是否有再remove
                List<string> tempKeys = new List<string>();
                foreach (var data in table)
                {
                    if (trimfield.Contains(data.Key))
                    {
                        tempKeys.Add(data.Key);
                    }
                }
                foreach (var k in tempKeys)
                {
                    table.Remove(k);
                }
                mr.Data = table;
            }
            return mr;

        }

        #endregion

        #region Proxy的处理方法

        /// <summary>
        /// 抽象函数不实现，由继承的handler实现验证。
        /// </summary>
        /// <param name="token">需要验证的信息</param>
        /// <param name="code">返回的code</param>
        /// <param name="msg">返回的信息</param>
        /// <returns></returns>
        public abstract bool Validate(string token, out int code, out string msg);
        /// <summary>
        /// 抽象的索引器，获取模块名。
        /// </summary>
        public abstract string ModuleNamespace { get; }

        /// <summary>
        /// 加载目标的模块组件，主要是传入namespace那些东西获取assembly
        /// </summary>
        /// <returns></returns>
        public void InitModule()
        {

            // 检查是否已经初始化
            if (ProxyCache.Get("__HASINITALIZE__") != null && (bool)ProxyCache.Get("__HASINITALIZE__"))
                return;

            // 从抽象的索引器获取modulenamespace,然后获取下面所有的类
            Type[] allclasses = Assembly.Load(ModuleNamespace).GetTypes();

            // 记录在cache里面
            ProxyCache.Set("__ALLCLASSES__", allclasses);

            #region 获取所有的class对象实例，然后获取其所有有webproxyattribute的方法

            foreach (var c in allclasses)
            {
                // 先获取一个实例，但是暂时不缓存。
                var cObject = c.Assembly.CreateInstance(c.FullName);
                bool isProxyClass = false;


                // 获取所有的方法，检查有没有attribute
                var methods = c.GetMethods();
                foreach (var m in methods)
                {
                    var curAttr = m.GetCustomAttributes(typeof(WebproxyAttribute), false);
                    if (curAttr == null || curAttr.Length == 0)
                        continue;
                    isProxyClass = true;

                    // 记录到proxycache
                    ProxyCache.Set(string.Format("{0}_{1}", c.Name, m.Name), m);
                }

                // 记录实例对象
                if (isProxyClass)
                    ProxyCache.Set(c.Name, cObject);
            }

            #endregion

            #region 保存初始化信息

            ProxyCache.Set("__HASINITALIZE__", true);

            #endregion

        }

        /// <summary>
        /// 构造函数，在构造的时候会自动初始化目标模块。但是初始化的过程需要继承实现
        /// </summary>
        public ProxyInvoker()
        {
            InitModule();
        }

        /// <summary>
        /// 调用模块的接口
        /// </summary>
        /// <param name="module">模块名</param>
        /// <param name="method">方法名</param>
        /// <param name="args">参数</param>
        public MetaResult InvokeMethod(HttpContext ctx, string module, string method, params object[] args)
        {
            // 从ProxyCache里面获取方法以减少反射
            var inv = (MethodInfo)ProxyCache.Get(string.Format("{0}_{1}", module, method));
            var obj = ProxyCache.Get(module);

            #region 判断是否含有webproxy特性

            var attribute = this._getAttribute(inv);
            if (attribute == null)
            {
                return new MetaResult()
                {
                    Success = false,
                    Code = 0x03,
                    Data = null,
                    Message = "调用错误：非法的调用接口"
                };
            }

            #endregion

            #region 如果有特性的话则按照特性不同而输出

            // 检查调用间隔
            if (attribute.CallInterval != 0)
            {
            }

            #region 检查是否需要验证session信息
            if (attribute.UseValidate)
            {
                // 获取token属性
                string token = ctx.Request["__tkey"];
                if (!string.IsNullOrEmpty(token))
                {
                    int code = 0;
                    string msg = string.Empty;

                    if (!this.Validate(token, out code, out msg))
                    {
                        return new MetaResult()
                        {
                            Success = false,
                            Code = code,
                            Message = msg
                        };
                    }
                }
                else
                {
                    return new MetaResult()
                    {
                        Success = false,
                        Code = 0x05,
                        Message = "登录信息不存在，请重新登录"
                    };
                }
            }
            #endregion

            // 调用获取结果
            MetaResult result = new MetaResult();
            try
            {
                object data = inv.Invoke(obj, args);
                // 移动端输出需要压缩，所以不用缩进
                result.IsPretty = !attribute.ForMobile;
                // 如果调用没有意外的话就可以获取其值
                result.Success = true;
                result.Code = 0x200;
                result.Message = "";
                result.Data = data;
                // 如果是移动端的话，需要检查是否trim数据
                if (attribute.ForMobile && attribute.IgnoreField != null)
                {
                    result = this._trimResultData(result, attribute.IgnoreField);
                }
                return result;
            }
            catch (ProxyMethodInvokeException pex)
            {
                return new MetaResult()
                {
                    Success = false,
                    Code = pex.ProxyErrorCode,
                    Data = null,
                    Message = pex.Message
                };
            }
            catch (Exception ex)
            {
                return new MetaResult()
                {
                    Success = false,
                    Code = 0x04,
                    Data = null,
                    Message = "调用失败：" + ex.Message
                };
            }

            #endregion

        }

        /// <summary>
        /// 将输出数据转换为Json
        /// </summary>
        /// <param name="r">输出的结果，metaresult</param>
        /// <returns></returns>
        public void ParseResult(MetaResult r)
        {
            _response.ContentType = "application/json";
            _response.Write(r.Parse());
        }

        #endregion

        #region HttpHandler的实现

        public void ProcessRequest(HttpContext context)
        {
            // 获取调用的模块和方法
            string module = context.Request["__module"];
            string method = context.Request["__call"];

            #region 获取response

            if (this._response == null)
                this._response = context.Response;

            #endregion

            #region 检查调用的状态
            if (string.IsNullOrEmpty(module) || string.IsNullOrEmpty(method))
            {
                this.ParseResult(new MetaResult()
                    {
                        Success = false,
                        Code = 0x001,
                        Data = null,
                        Message = "调用失败：错误的模块或者方法名"
                    });
                return;
            }
            #endregion

            #region 获取调用的参数

            // 参数作为UTF-8的base64加密，先解密一下
            string callparaString = context.Request["__parameters"];
            object callParams = null;

            if (!string.IsNullOrEmpty(callparaString))
            {
                var paraBytes = Convert.FromBase64String(callparaString);
                callparaString = Encoding.UTF8.GetString(paraBytes);

                callParams = JsonConvert.DeserializeObject<object[]>(callparaString);
            }

            #endregion

            #region 调用接口

            try
            {
                // 需要检查这次调用有没有参数
                if (callParams != null)
                    this.ParseResult(this.InvokeMethod(context, module, method, callParams));
                else
                    this.ParseResult(this.InvokeMethod(context, module, method));
            }
            catch (Exception ex)
            {
                this.ParseResult(new MetaResult()
                {
                    Success = false,
                    Code = 0x002,
                    Data = null,
                    Message = "调用失败：" + ex.ToString()
                });
            }

            #endregion
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

        #endregion
    }
}
